CloudWatch Logsの特定文字を検知してログ内容を通知するLambda Function
CloudWatch Logsへの特定文字の書き込みを検知する仕組みとして、メトリクスフィルターを利用しCloudWatch Alarmを設定する構成は多くあります。こちらの構成は、CloudWatch Alarmの状態変化を示す通知となり、検知したログデータそのものを通知に含めることはできません。
▲ メトリクスフィルターを利用したCloudWatch Alarmの通知例
サードパーティのログインテグレーション等を利用せず、CloudWatch Logsのログデータを通知に含めたいといった場合は少々作り込みが必要です。今回はCloudWatch Logsにサブスクリプションフィルターを設定し、検知したログデータを通知するLambda Funcionを作成してみました。
前提
通知先はChatworkとし、通知に利用するSNSトピック、Lambda Functionは以下を利用します。
構成
前提に記載のLambda Function等を利用するため、以下赤枠内が今回のスコープです。CloudWatch Logsのサブスクリプションフィルター設定と、SNSトピックにログデータを送信するLambda Functionの作成を本ブログで行います。
構築
Lambda Function作成
ランタイムはPython 3.8
で、以下Lambda Functionの設定です。
関数コード
import logging import json import base64 import gzip import boto3 import os logger = logging.getLogger() logger.setLevel(logging.INFO) def lambda_handler(event, context): sns_topic_arn = os.environ['SNS_TOPIC_ARN'] # 環境変数よりSNSトピックARN取得 sns_client = boto3.client('sns') # CloudWatchLogsからのデータはbase64エンコードされているのでデコード decoded_data = base64.b64decode(event['awslogs']['data']) # バイナリに圧縮されているため展開 json_data = json.loads(gzip.decompress(decoded_data)) logger.info("EVENT: " + json.dumps(json_data)) # ログデータ取得 message = json_data['logEvents'][0]['message'] # SNS件名設定 log_group = json_data['logGroup'] log_stream = json_data['logStream'] #ロググループ名、ログストリーム名をsubjectに設定 subject = log_group + ' ' + log_stream response = sns_client.publish( TopicArn = sns_topic_arn, Subject = subject, Message = json.dumps(message) )
環境変数
本Lambda Functionはサブスクリプションフィルター設定により、Lambda Functionに送信されたCloudWatch LogsのログデータをSNSトピックに送信します。利用するSNSトピックのARNを環境変数に指定しています。
CloudWatch Logsのサブスクリプションフィルター設定により、実行されるLambda Functionは{ "awslogs": {"data":"BASE64ENCODED_GZIP_COMPRESSED_DATA"} }
イベントデータを受け取ります。data
キーはBase64 でエンコードされており、gzip 形式で圧縮されています。ログデータを取得するため、デコード、展開を実施します。
その後、任意の件名(ここでは、ロググループ名 + ログストリーム名)を指定し、ログデータをSNSトピックに送信しています。
なお、後続のLambda Function(前提に記載のChatworkにメッセージを送信するLambda Function)でjson.load
を行っており、そちらのLambda Functionに修正を加えないとタブ等の制御文字が扱えないため、publish
メソッド呼び出しの際にjson.dumps
でエスケープしてログデータを送信しています。通知内容等この辺りをカスタマイズする際は、後続処理も考慮した修正が必要です。(今回は後続のLambda Functionの修正は行っていません。)
CloudWatch Logsサブスクリプションフィルター設定
該当のロググループに、Lambdaサブスクリプションフィルターを設定します。任意のフィルター名で先程作成したLambda Functionを送信先に指定します。
ここでは、フィルタパターンを"ERROR"とし、特定の書き込み(ここではERROR)を含むログデータのみLambda Functionに送信するようにしています。フィルターの詳細については、以下を確認ください。
▲ Lambdaサブスクリプションフィルターの設定
CloudWatch Logsにてサブスクリプションフィルターの設定を行うと、該当のLambda Functionにトリガー等が付与されます。
通知確認
Lambdaサブスクリプションフィルターを設定したロググループに、フィルタパターンに合致する書き込みを行いChatworkへの通知を確認します。put-log-eventsでログの書き込みが可能です。
▲ ERRORを含むロギングを実施
Chatwork通知に、ログデータが含まれていることを確認できました。
さいごに
EventBridgeのイベントデータにログデータがあれば、イベントパターンの定義のみで作り込み不要でログデータを取得できるかな?とも考えましたが、CloudWatch Logsは、CloudTrailを介してイベントとなり、ログデータは含まれていませんでした。
APIコールのイベントとなり、以下のようなイベントデータでした。
CloudTrailを介したCloudWatch Logsのイベント例
{ "version":"0", "id":"56ae36c7-dfb1-6966-2778-52434364d2a5", "detail-type":"AWS API Call via CloudTrail", "source":"aws.logs", "account":"XXXXXXXXXXXX", "time":"2021-01-20T00:21:10Z", "region":"ap-northeast-1", "resources":[ ], "detail":{ "eventVersion":"1.08", "userIdentity":{ "type":"AssumedRole", "principalId":"AA:test-error-write", "arn":"arn:aws:sts::XXXXXXXXXXXX:assumed-role/LambdaRole/test-error-write", "accountId":"XXXXXXXXXXXX", "accessKeyId":"", "sessionContext":{ "sessionIssuer":{ "type":"Role", "principalId":"AA", "arn":"arn:aws:iam::XXXXXXXXXXXX:role/LambdaRole", "accountId":"XXXXXXXXXXXX", "userName":"LambdaRole" }, "webIdFederationData":{ }, "attributes":{ "mfaAuthenticated":"false", "creationDate":"2021-01-20T00:21:01Z" } } }, "eventTime":"2021-01-20T00:21:10Z", "eventSource":"logs.amazonaws.com", "eventName":"CreateLogStream", "awsRegion":"ap-northeast-1", "sourceIPAddress":"52.195.16.227", "userAgent":"awslambda-worker/1.0 rusoto/0.42.0 rust/1.47.0 linux", "requestParameters":{ "logGroupName":"/aws/lambda/test-error-write", "logStreamName":"2021/01/20/[$LATEST]e0efc2e860504dd1b7ea0f8a8b2c1539" }, "responseElements":null, "requestID":"f603bdb5-8647-47c3-8cf9-b2d318cf947b", "eventID":"121a21e5-69e4-45bc-94c8-31a2aaeea231", "readOnly":false, "eventType":"AwsApiCall", "apiVersion":"20140328", "managementEvent":true, "eventCategory":"Management" } }
作り込み不要でログデータの通知を行えるようなアップデートに期待したいと思います。